﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="BaseRepositoryImpl.cs" company="Rabid Dog Labs">
//   Rabid Dog Labs
// </copyright>
// <summary>
//   Defines the BaseRepositoryImpl type.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using log4net;
using NHibernate;
using NHibernate.Criterion;
using TeamCommunitySolution.Domain.DataLayer.Base;
using TeamCommunitySolution.Repositories.Providers;

namespace TeamCommunitySolution.Repositories.Base
{
    /// <summary>
    /// Implementation of the Base Repository.
    /// </summary>
    public class BaseRepositoryImpl : BaseRepository
    {
        /// <summary>
        /// The log instance.
        /// </summary>
        private static readonly ILog _log = LogManager.GetLogger(typeof(BaseRepository));

        /// <summary>
        /// Prevents a default instance of the <see cref="BaseRepositoryImpl"/> class from being created. 
        /// Initializes a new instance of the <see cref="BaseRepositoryImpl"/> class.
        /// </summary>
        private BaseRepositoryImpl()
        {
        }

        /// <summary>
        /// Creates the instance.
        /// </summary>
        /// <returns>Base Repository reference.</returns>
        public static BaseRepository CreateInstance()
        {
            return new BaseRepositoryImpl();
        }

        #region BaseRepository Members

        /// <summary>
        /// Saves the user.
        /// </summary>
        /// <param name="user">The user to save.</param>
        public void SaveUser(SystemUser user)
        {
            using (ISession session = Nhibernate.Factory.OpenSession())
            {
                var scope = session.BeginTransaction();
                try
                {
                    if (user.CreationDate.Equals(DateTime.MinValue))
                    {
                        user.CreationDate = DateTime.Now;
                    }

                    if (user.DateOfBirth.Equals(DateTime.MinValue))
                    {
                        user.DateOfBirth = DateTime.Now;
                    }

                    if (user.LastLoginDate.Equals(DateTime.MinValue))
                    {
                        user.LastLoginDate = DateTime.Now;
                    }

                    if (user.LastPasswordChanged.Equals(DateTime.MinValue))
                    {
                        user.LastPasswordChanged = DateTime.Now;
                    }

                    session.Persist(user);
                    scope.Commit();
                }
                catch (SqlException ex)
                {
                    scope.Rollback();
                    string message = "Unable to save object [SystemUser] in the datastore";
                    this.HandleException(message, ex);
                }
            }
        }

        /// <summary>
        /// Gets the user by field.
        /// </summary>
        /// <param name="fieldName">Name of the field.</param>
        /// <param name="fieldValue">The field value.</param>
        /// <returns>System User instance.</returns>
        public SystemUser GetUserByField(string fieldName, string fieldValue)
        {
            using (ISession session = Nhibernate.Factory.OpenSession())
            {
                ICriteria crit = session.CreateCriteria(typeof(SystemUser));
                crit.Add(Expression.Eq(fieldName, fieldValue));

                IList<SystemUser> results = crit.List<SystemUser>();

                if (results.Count > 1)
                {
                    throw new BaseRepositoryException(string.Format("Duplicate objects [SystemUser] found in the datastore. Field: {0}, Value: {1}", fieldName, fieldValue));
                }

                if (results.Count == 0)
                {
                    return null;
                }

                return results[0];
            }
        }

        /// <summary>
        /// Saves the message.
        /// </summary>
        /// <param name="messageInbox">The message inbox.</param>
        /// <param name="messageOutbox">The message outbox.</param>
        public void SaveMessage(MessageInbox messageInbox, MessageOutbox messageOutbox)
        {
            using (ISession session = Nhibernate.Factory.OpenSession())
            {
                var scope = session.BeginTransaction();
                try
                {
                    if (messageInbox.DateSent.Equals(DateTime.MinValue))
                    {
                        messageInbox.DateSent = DateTime.Now;
                    }

                    if (messageOutbox.DateSent.Equals(DateTime.MinValue))
                    {
                        messageOutbox.DateSent = DateTime.Now;
                    }

                    session.Persist(messageInbox);
                    session.Persist(messageOutbox);
                    scope.Commit();
                }
                catch (SqlException ex)
                {
                    scope.Rollback();
                    string message = "Unable to save object [MessageInbox] and [MessageOutbox] in the datastore";
                    this.HandleException(message, ex);
                }
            }
        }

        /// <summary>
        /// Gets the inbox message.
        /// </summary>
        /// <param name="id">The id of the message.</param>
        /// <returns>Message Inbox instance.</returns>
        public MessageInbox GetInboxMessage(string id)
        {
            MessageInbox output = null;
            using (ISession session = Nhibernate.Factory.OpenSession())
            {
                var scope = session.BeginTransaction();
                try
                {
                    output = session.Get<MessageInbox>(id);
                    scope.Commit();
                }
                catch (SqlException ex)
                {
                    scope.Rollback();
                    string message = "Unable to find object [MessageInbox] in the datastore";
                    this.HandleException(message, ex);
                }
            }

            if (output == null)
            {
                throw new BaseRepositoryException(string.Format("Could not find the MessageInbox with id {0}", id));
            }

            return output;
        }

        /// <summary>
        /// Gets the outbox message.
        /// </summary>
        /// <param name="id">The id of the message.</param>
        /// <returns>Message Outbox Instance.</returns>
        public MessageOutbox GetOutboxMessage(string id)
        {
            MessageOutbox output = null;
            using (ISession session = Nhibernate.Factory.OpenSession())
            {
                var scope = session.BeginTransaction();
                try
                {
                    output = session.Get<MessageOutbox>(id);
                    scope.Commit();
                }
                catch (SqlException ex)
                {
                    scope.Rollback();
                    string message = "Unable to find object [MessageOutbox] in the datastore";
                    this.HandleException(message, ex);
                }
            }

            if (output == null)
            {
                throw new BaseRepositoryException(string.Format("Could not find the MessageOutbox with id {0}", id));
            }

            return output;
        }

        /// <summary>
        /// The save team.
        /// </summary>
        /// <param name="team">The team to save.</param>
        public void SaveTeam(ref Team team)
        {
            using (ISession session = Nhibernate.Factory.OpenSession())
            {
                var scope = session.BeginTransaction();
                try
                {
                    team = (Team)session.SaveOrUpdateCopy(team);
                    scope.Commit();
                }
                catch (SqlException e)
                {
                    scope.Rollback();
                    string message = "Unable to save object [Team] to the datastore";
                    this.HandleException(message, e);
                }
            }
        }

        /// <summary>
        /// Gets the team by field.
        /// </summary>
        /// <param name="fieldName">Name of the field.</param>
        /// <param name="fieldValue">The field value.</param>
        /// <returns>The team instance.</returns>
        public Team GetTeamByField(string fieldName, string fieldValue)
        {
            using (ISession session = Nhibernate.Factory.OpenSession())
            {
                ICriteria crit = session.CreateCriteria(typeof(Team));
                crit.Add(Expression.Eq(fieldName, fieldValue));

                IList<Team> results = crit.List<Team>();

                if (results.Count > 1)
                {
                    throw new BaseRepositoryException(string.Format("Duplicate objects [Team] found in the datastore. Field: {0}, Value: {1}", fieldName, fieldValue));
                }

                if (results.Count == 0)
                {
                    throw new BaseRepositoryException(string.Format("Could not find the object [Team) using field {0} with value {1}", fieldName, fieldValue));
                }

                return results[0];
            }
        }

        /// <summary>
        /// Saves the team invite.
        /// </summary>
        /// <param name="invite">The invite.</param>
        public void SaveTeamInvite(TeamInvite invite)
        {
            using (ISession session = Nhibernate.Factory.OpenSession())
            {
                var scope = session.BeginTransaction();
                try
                {
                    session.Persist(invite);
                    scope.Commit();
                }
                catch (SqlException e)
                {
                    scope.Rollback();
                    string message = "Unable to save object [TeamInvite] to the datastore";
                    this.HandleException(message, e);
                }
            }
        }

        /// <summary>
        /// Searches the name of the system user by.
        /// </summary>
        /// <param name="name">The name of the user.</param>
        /// <returns>List of users.</returns>
        public IList<SystemUser> SearchSystemUserByName(string name)
        {
            using (ISession session = Nhibernate.Factory.OpenSession())
            {
                ICriteria crit = session.CreateCriteria(typeof(SystemUser));
                crit.Add(Expression.Or(Expression.Like("FirstName", name + "%"), Expression.Like("LastName", name + "%")));

                IList<SystemUser> userList = crit.List<SystemUser>();

                return userList;
            }
        }

        /// <summary>
        /// Gets the team invite by field.
        /// </summary>
        /// <param name="fieldName">Name of the field.</param>
        /// <param name="fieldValue">The field value.</param>
        /// <returns>Team Invite.</returns>
        public TeamInvite GetTeamInviteByField(string fieldName, string fieldValue)
        {
            using (ISession session = Nhibernate.Factory.OpenSession())
            {
                ICriteria crit = session.CreateCriteria(typeof(TeamInvite));
                crit.Add(Expression.Eq(fieldName, fieldValue));

                IList<TeamInvite> results = crit.List<TeamInvite>();

                if (results.Count > 1)
                {
                    throw new BaseRepositoryException(string.Format("Duplicate objects [TeamInvite] found in the datastore. Field: {0}, Value: {1}", fieldName, fieldValue));
                }

                if (results.Count == 0)
                {
                    throw new BaseRepositoryException(string.Format("Could not find the object [TeamInvite] using field {0} with value {1}", fieldName, fieldValue));
                }

                return results[0];
            }   
        }

        /// <summary>
        /// Gets the team invite by field.
        /// </summary>
        /// <param name="id">The id.</param>
        public void DeleteTeamInvite(string id)
        {
            using (ISession session = Nhibernate.Factory.OpenSession())
            {
                var scope = session.BeginTransaction();
                try
                {
                    TeamInvite teamInvite = session.Get<TeamInvite>(id);
                    session.Delete(teamInvite);
                    scope.Commit();
                }
                catch (SqlException ex)
                {
                    scope.Rollback();
                    string message = "Unable to remove object [Team Invite] in the datastore";
                    this.HandleException(message, ex);
                }
            }
        }
        #endregion

        /// <summary>
        /// Handles the exception.
        /// </summary>
        /// <param name="message">The message.</param>
        /// <param name="ex">The exception.</param>
        private void HandleException(string message, Exception ex)
        {
            _log.Error(message, ex);
            throw new BaseRepositoryException(message, ex);
        }
    }
}
